/*

    This file is part of Hometree.

    Hometree is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Hometree is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Hometree.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2011,2012 Roman Held

*/

/********************************************************************\
                          Includes
\********************************************************************/
#include <QDebug>
#include <QHostAddress>
#include "htrssession.h"
#include "htrsmap.h"
#include "htrsresource.h"
#include "htrsmonitor.h"
#include "htr/htrglobal.h"
#include "htr/htrlog.h"

/********************************************************************\
                          Macros
\********************************************************************/
#define IDENT "SESS"
#define PROTOCOL_VERSION 1

/********************************************************************\
                          Implementation
\********************************************************************/
/*--------------------------------------------------------------------
*/
HtrsSession::HtrsSession(QTcpSocket *socket, HtrsMap *resources)
  : QObject(), mSocket(this)
{
  Q_ASSERT(socket);
  Q_ASSERT(resources);

  mMonitor           = NULL;
  mClientNetProtocol = 0;
  mServerNetProtocol = PROTOCOL_VERSION;
  mResources         = resources;
 
  connect(&mSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
          this, SLOT(newSocketState()));
  connect(&mSocket,SIGNAL(readyRead()),
          this, SLOT(processCommand()));

  mSocket.setSocket(socket);
  HtrLog::instance()->info(IDENT,QString("Session created (Client: %1)").arg(mSocket.ident()));

  Q_ASSERT(mSocket.isConnected()); 
}

/*--------------------------------------------------------------------
*/
HtrsSession::~HtrsSession()
{
  HtrLog::instance()->info(IDENT,QString("Session deleted"));
  mSocket.close();
  if (mMonitor)
    mMonitor->deleteLater();
}

/*--------------------------------------------------------------------
*/
bool HtrsSession::sendSms(const QString &shortMessage)
{
  if (!mSocket.isConnected())
    return false;

  QDataStream &sendStream = mSocket.prepareStream();
  sendStream << (qint32)Htr::Net::ClSmsMsg;
  sendStream << shortMessage;
  mSocket.writeStream();
  return true;
}

/*--------------------------------------------------------------------
*/
void HtrsSession::shutdown()
{
  HtrLog::instance()->info(IDENT,QString("Session shutdown"));
  mSocket.flush();

  // TODO: Client-Msg?
  deleteLater();
}

/*--------------------------------------------------------------------
*/
void HtrsSession::processCommand()
{
  int maxLoopCount = 5; // Flood unterbinden:
  while (mSocket.next() && (maxLoopCount-- > 0)) {
    QDataStream &stream = mSocket.readStream();
    qint32 msg;
    stream >> msg;

    if (msg < 0 || msg >= Htr::Net::InvalidMsg) {
      qWarning() << "HtrsSession::processCommand: skip invalid Command!";
      break;
    }

    switch(msg) {
      case Htr::Net::ClWelcomeMsg : {
        handleWelcomeMsg(stream);
      } break;
      case Htr::Net::ClIdentMsg : {
        handleInitMsg(stream);
      } break;
      case Htr::Net::ClResourceMsg : {
        handleResourceMsg(stream);
      } break;
      case Htr::Net::ClMonitorMsg : {
        handleMonitorMsg(stream);
      } break;
      case Htr::Net::ClKeepAliveMsg : {
      } break;
    }
  }
}

/*--------------------------------------------------------------------
*/
void HtrsSession::newSocketState()
{
  if (!mSocket.isConnected()) {
    deleteLater();
  }
}

/*--------------------------------------------------------------------
*/
void HtrsSession::resourceBroadCast(const QByteArray &msg)
{
  if (!mSocket.isConnected())  // zu spaet
    return;

  HtrLog::instance()->info(IDENT,QString("Redirecting Message"));
  mSocket.writeByteArray(msg);
}

/*--------------------------------------------------------------------
*/
void HtrsSession::handleWelcomeMsg(QDataStream &stream)
{
  stream >> mClientNetProtocol;
  HtrLog::instance()->info(IDENT,QString("Client uses protocol V%1").arg(mClientNetProtocol));

  // Answer: my version:
  QDataStream &sendStream = mSocket.prepareStream();
  sendStream << (qint32)Htr::Net::ClWelcomeMsg;
  sendStream << mServerNetProtocol;
  mSocket.writeStream();
}

/*--------------------------------------------------------------------
*/
void HtrsSession::handleInitMsg(QDataStream &stream)
{
  QString url;
  qint32  resType;
  stream >> url;
  stream >> resType;
  HtrLog::instance()->info(IDENT,QString("Init new resource: %1").arg(url));
  bool created;
  HtrsResource *res = mResources->access(url, (Htr::ResourceType)resType, created);
  res->disconnect(this);
  connect(this,  SIGNAL(resourceMsg(qint32, HtrManagedSocket*)),
          res, SLOT(cbMsg(qint32,HtrManagedSocket*)));

  QDataStream &sendStream = mSocket.prepareStream();
  sendStream << (qint32)Htr::Net::ClIdentMsg;
  sendStream << url;
  sendStream << res->id();
  sendStream << (qint32)res->type();
  mSocket.writeStream();

  if (created) { // dem Sender mitteilen, dass ER die Ressource erstellt hat:
    QDataStream &sendStream = mSocket.prepareStream();
    sendStream << (qint32)Htr::Net::ClResourceMsg;
    sendStream << res->id();
    sendStream << (qint32)Htr::Net::CreatedMsg;
    mSocket.writeStream();
  }
}

/*--------------------------------------------------------------------
*/
void HtrsSession::handleResourceMsg(QDataStream &stream)
{
  qint32  resId;
  stream >> resId;
  emit resourceMsg(resId,&mSocket);
}

/*--------------------------------------------------------------------
*/
void HtrsSession::handleMonitorMsg(QDataStream &)
{
  Q_ASSERT(!mMonitor); // Meldung darf nur einmalig gesendet werden
  Q_ASSERT(mResources);
  mMonitor = new HtrsMonitor(mResources);
  connect(mMonitor,SIGNAL(sendMsg(const QByteArray &)),
          this, SLOT(resourceBroadCast(const QByteArray &)));

  HtrLog::instance()->info(IDENT,QString("Client %1 is monitoring").arg(mSocket.ident()));

  mMonitor->sendIdents();
}

